
#include <qpainter.h>
#include <qpixmap.h>
#include <qimage.h>

#include <cmath>
#include <iostream>

#include "EMapAccess.hpp"
#include "MIFparser.hpp"

const unsigned int back_color = 0x00ffffff;

namespace ModuleEMapData
{
  static float START_X=NAN, START_Y=NAN;
  static float VP_WID=NAN,  VP_HGT=NAN;

  static const char *fname_format="/tmp/gps_%d_G%d-%d.bmp";
  static int   zoom_in_factor;
};

using namespace ModuleEMapData;
using namespace std;


// 在painter上面画出图元primi
static void drawPrimitive( QPainter &painter, primitive &primi, int, int );
// 清理primi里面的动态内存
static void clean( primitive &primi );
// 保存绘制好的地图
static void save2fs( QPixmap *orig_img, int orig_wid, int orig_heit, int zin_factor );

// 将经纬度转换到坐标点
void ModuleEMapData::set_map_range( float longitude_of_lefttop, float latitude_of_lefttop, float longitude_range, float latitude_range )
{
  START_X = longitude_of_lefttop;
  START_Y = latitude_of_lefttop;

  VP_WID = longitude_range;
  VP_HGT = latitude_range;
}

void ModuleEMapData::set_filename_format( const char *filename_fmt )
{
  fname_format = filename_fmt;
}

bool ModuleEMapData::init( const char *mif_name, int orig_wid, int orig_heit, int zin_factor )
{
  // 是否已经设置好匹配的经纬度
  if( isnan( START_X ) || isnan( START_Y ) ||
      isnan( VP_WID )  || isnan( VP_HGT ) )
    {
      cerr << "please call set_map_range first to set the longitudes and latitudes" << endl;
      return false;
    }

  // 打开mif文件
  ifstream file( mif_name );
  if( !file )
    {
      cerr << "mif file " << mif_name << " can not be open!\n";
      return false;		// 文件打开失败
    }

  // 成功，设置全局变量
  zoom_in_factor = zin_factor;

  // 初始化图元流
  PrimitiveStream *ps = new MIFparser( file );
  primitive cur_primi;

  // 计算画布大小
  int seg_count = 1<<zin_factor;
  int real_width = orig_wid * seg_count, real_height = orig_heit * seg_count;

  // 准备好作画的画板，画上白色背景
  QPixmap *canvas = new QPixmap( real_width, real_height );
  canvas->fill( back_color );

  // 开始逐个绘制图元
  QPainter the_painter;
  the_painter.begin( canvas );
  while( !ps->eof() )
    {
      memset( &cur_primi, 0, sizeof(cur_primi) );

      (*ps) >> cur_primi;
      drawPrimitive( the_painter, cur_primi, orig_wid, orig_heit );
      clean( cur_primi );
    }
  the_painter.end();

  // 将地图以图片格式保存到文件系统中
  save2fs( canvas, orig_wid, orig_heit, zin_factor );

  delete canvas;
  delete ps;
  return true;
}

void ModuleEMapData::clear( void )
{
  int seg_count;

  char *fname_buffer = new char[ strlen(fname_format) + 3*sizeof(int)*3 ];
  for( int i=0; i <= zoom_in_factor; i++ )
    {
      seg_count = 1 << i;
      for( int j=0; j < seg_count; ++j )
	for( int k = 0;k < seg_count; ++k )
	  {
	    sprintf( fname_buffer, fname_format, i, j, seg_count-1-k );
	    // 删除此文件
	    unlink( fname_buffer );
	  }
    }
  delete[] fname_buffer;
}

inline float ratio( float pos, float begin, float width )
{
  return (pos-begin)/width;
}

inline int real_pos( int width, float ratio )
{
  return width*ratio;
}

static void convert( coord *pcoord, int count, int VP_WIDTH, int VP_HEIGHT )
{
  coord *pend = pcoord + count;
  int val,
    wid = VP_WIDTH * (1<<zoom_in_factor),
    hgh = VP_HEIGHT *(1<<zoom_in_factor);
  for( coord* p=pcoord; p < pend; ++p )
    {
      val = real_pos( wid, ratio( p->x, START_X, VP_WID ) );
      //cerr << val << ' ';
      p->x = (float&)(  val );
      val = hgh - real_pos( hgh, ratio( p->y, START_Y, VP_HGT ) );
      //cerr << val << endl;
      p->y = (float&)(  val );
    }
}

static void drawPrimitive( QPainter &painter, primitive &primi, int orig_wid, int orig_heit )
{
  QPen pen;
  QBrush brush;

  // 设置画笔画刷
  for( primitive_attr_idx *i=primi.attribute;
       i < primi.attribute+MAX_ATTRIBUTE; ++i )
    {
      if( i->_M_node != NULL )
	{
	  if( (*i)->attr_name == PEN )
	    {
	      pen.setWidth( (*i)->attr_value[0] );
	      pen.setColor( QColor( (*i)->attr_value[2] ) );

	      painter.setPen( pen );
	    }
	  else if( (*i)->attr_name == BRUSH )
	    {
	      brush.setColor( QColor( (*i)->attr_value[ 1 ] ) );
	      brush.setStyle( Qt::SolidPattern );
	      pen.setColor( QColor( (*i)->attr_value[ 2 ] ) );

	      painter.setPen( pen );
	      painter.setBrush( brush );
	    }
	}
    }

  QPointArray parr;
  QPoint point;
  switch( primi.type )
    {
    case ModuleEMapData::PRIMITIVE_POLYLINE:
      convert( primi.coord_data, primi.coord_count, orig_wid, orig_heit );
      parr.setPoints( primi.coord_count, (const QCOORD*)primi.coord_data );
      painter.drawPolyline( parr );
      break;
    case ModuleEMapData::PRIMITIVE_REGION:
      convert( primi.coord_data, primi.coord_count, orig_wid, orig_heit );
      parr.setPoints( primi.coord_count, (const QCOORD*)primi.coord_data );
      painter.drawPolygon( parr, true );
      break;
    default:
      ;
    }
}

static void clean(primitive& primi)
{
  if (primi.type == ModuleEMapData::PRIMITIVE_REGION)
    {
      primitive *p = (primitive*)primi.other_data, *next;
      while( p != NULL )
	{
	  delete[] p->coord_data;
	  next = (primitive*)p->other_data;
	  delete p;
	  p = next;
	}
      delete[] primi.coord_data;
    }
  else
    {
      delete[] primi.coord_data;
    }
}

static void save2fs( QPixmap *orig_img, int orig_wid, int orig_heit, int zin_factor )
{
  QImage  scaled_img, split_img;
  int trim_x, trim_y;
  int seg_count;

  char *fname_buffer = new char[ strlen(fname_format) + 3*sizeof(int)*3 ]; // 这里没有做成常量有点不好，不过应该也没有大的问题，后面是3个整数会用到了空间
  for( int i=0; i <= zin_factor; ++i )
    {
      // 1 缩放图片到放大因子为i的大小，保存在scale_img里面
      seg_count = 1 << i;
      scaled_img = orig_img->convertToImage( ).smoothScale( orig_wid * seg_count, orig_heit * seg_count );

      for( int j=0; j < seg_count; ++j ) {
	trim_x = j * orig_wid;

	for( int k=0; k < seg_count; ++k )
	  {
	    trim_y = k * orig_heit;

	    // 计算出从哪里切出小块，然后切出来，保存
	    split_img = scaled_img.copy( trim_x, trim_y, orig_wid, orig_heit );
	    sprintf( fname_buffer, fname_format, i, j, seg_count-1-k );

	    split_img.save( fname_buffer, "BMP" );
	  }
      }
    }

  delete[] fname_buffer;
}
